package com.wbc.utils.wbcBeanUtil;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;


/**
 * 可以把任意对象的属性值 转为 另一个对象中任意属性值的工具类
 * 具体使用方法请观察 demo中的 Demo001 Demo002 和 Demo003
 * @author wangbingchen
 * @date 2024/05/14
 */
public class WbcBeanConvertUtil {

    /**
     * 配合 注解可以灵活匹配属性名注解可以灵活匹配属性名注解可以灵活匹配属性名
     * 把多个对象中的属性复制到一个对象中 属性名为 key 值为 value
     * @WbcField 注解可以指定属性名
     * @param clazz 对象类型
     * @param objects 多个对象
     * @return clazz 实例
     * @param <T> 类型参数
     */
    public static <T> T mergeObjects(Class<T> clazz, Object... objects) {
        return convertMapToObject(mergeObjectsToMap(objects), clazz);
    }

//    /**
//     * 把 obj 中的属性复制到 clazz 实例中 属性名为 key 值为 value
//     * @WbcField 注解可以指定属性名匹配时生效
//     * @param obj 对象
//     * @param clazz 对象类型
//     * @return clazz 实例
//     */
//    public static <T> T convert(Object obj, Class<T> clazz) {
//        //创建 clazz 实例
//        T targetObj = null;
//        try {
//            targetObj = clazz.newInstance();
//        } catch (InstantiationException | IllegalAccessException e) {
//            throw new RuntimeException(e);
//        }
//        //遍历 obj 中的属性，维护一个属性名到属性的映射
//        Map<String, Field> fieldMap = getFieldMap(obj.getClass());
//        //遍历 clazz 中的属性，维护一个属性名到属性的映射
//        Map<String, Field> targetFieldMap = getFieldMap(clazz);
//        //遍历 fieldMap，把属性名一样的属性赋值到 targetFieldMap 中对应 value targetField 中
//        for (Map.Entry<String, Field> entry : fieldMap.entrySet()) {
//            String key = entry.getKey();
//            Field field = entry.getValue();
//            Field targetField = targetFieldMap.get(key);
//            if (targetField != null) {
//                try {
//                    //设置属性可访问
//                    field.setAccessible(true);
//                    //获取 obj 中的属性值
//                    Object value = field.get(obj);
//                    //设置 targetObj 中的属性可访问
//                    targetField.setAccessible(true);
//                    //把 obj 中的属性值赋值到 targetObj 中
//                    targetField.set(targetObj, value);
//                } catch (IllegalAccessException e) {
//                    //忽略属性不可访问的情况
//                }
//            }
//        }
//        return targetObj;
//    }


    /**
     * 把多个对象中的属性复制到一个 Map 中 属性名为 key 值为 value
     * @WbcField 注解可以指定属性名
     * 多个对象中如有属性名冲突,加数字后缀,如 name 重复则 name1, name2, name3
     * @param objects 多个对象
     * @return Map 对象
     */
    public static Map<String, Object> mergeObjectsToMap(Object... objects) {
        Map<String, Object> map = new HashMap<>();
        for (Object obj : objects) {
            Field[] fields = obj.getClass().getDeclaredFields();
            //遍历 obj 中的属性，把属性名一样的属性赋值到 map 中
            for (Field field : fields) {
                WbcField wbcField = field.getAnnotation(WbcField.class);
                //设置属性可访问
                field.setAccessible(true);
                //获取 obj 中的属性值
                Object value = null;
                try {
                    value = field.get(obj);
                } catch (IllegalAccessException e) {
                    //忽略属性不可访问的情况
                }
                //判断属性是否存在于 map 中 如果存在名字后加数字1 2 3 4 5 6 7 8 9 10
                String key = wbcField == null? field.getName() : wbcField.value();
                int i = 1;
                while (map.containsKey(key)) {
                    key = field.getName() + i;
                    i++;
                }
                map.put(key, value);
            }
        }
        return map;
    }

    /**
     * 把 Map 中的属性复制到一个对象中 属性名为 key 值为 value
     * @WbcField 注解可以指定属性名匹配时生效
     * @param map Map对象
     * @param clazz 对象类型
     * @return 对象
     */
    public static <T> T convertMapToObject(Map<String, Object> map, Class<T> clazz) {
        T obj = null;
        //维护一个属性名到属性的映射
        Map<String, Field> fieldMap = getFieldMap(clazz);
        //创建 clazz 实例
        try {
            obj = clazz.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            String key = entry.getKey();
            Object value = entry.getValue();
            try {
                Field field = fieldMap.get(key);
                field.setAccessible(true);
                field.set(obj, value);
            } catch (IllegalAccessException e) {
                //忽略属性不可访问的情况
            } catch (NullPointerException e) {
                //忽略属性不存在的情况
            }
        }
        return obj;
    }

    /**
     * 建立 属性名和属性对象的映射
     * @param clazz         类对象
     * @param fieldMap      属性名和属性对象的映射
     * @return
     * @param <T>            类型参数
     */
    private static <T> Map<String, Field> getFieldMap(Class<T> clazz, Map<String, Field> fieldMap) {
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            WbcField wbcField = field.getAnnotation(WbcField.class);
            String key = wbcField == null? field.getName() : wbcField.value();
            fieldMap.put(key, field);
        }
        return fieldMap;
    }

    /**
     * 建立 属性名和属性对象的映射
     * @param clazz 类对象
     * @return 属性名和属性对象的映射
     * @param <T>    类型参数
     */
    private static <T> Map<String, Field> getFieldMap(Class<T> clazz) {
        Map<String, Field> fieldMap = new HashMap<>();
        return getFieldMap(clazz, fieldMap);
    }


}


